package cn.edu.nchu.comic_strip.database;

import android.content.Context;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.sqlite.db.SupportSQLiteDatabase;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import cn.edu.nchu.comic_strip.R;
import cn.edu.nchu.comic_strip.constants.Constants;
import cn.edu.nchu.comic_strip.database.dao.BookDao;
import cn.edu.nchu.comic_strip.database.dao.PageDao;
import cn.edu.nchu.comic_strip.entity.Book;
import cn.edu.nchu.comic_strip.entity.Page;

/**
 * @className: AppDatabase
 * @description: Database class for the application, defines entities and handles database creation and version management.
 *              This database contains Book and Page entities.
 *              Singleton pattern is used to ensure only one instance of the database exists.
 *              Initializes database with predefined SQL statements from raw resource file.
 * @Author: Kuangkeli
 * @date: 2024/05/24 17:45
 **/
@Database(entities = {Book.class, Page.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
    public abstract BookDao getBookDao();

    public abstract PageDao getPageDao();

    private static final String TAG = "AppDatabase";
    private static volatile AppDatabase INSTANCE;
    private static Context context;

    /**
     * Get the singleton instance of the database.
     *
     * @param context The context of the caller.
     * @return The singleton instance of AppDatabase.
     */
    public static AppDatabase getDatabase(final Context context) {
        if (INSTANCE == null) {
            synchronized (AppDatabase.class) {
                if (INSTANCE == null) {
                    AppDatabase.context = context;
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, Constants.DATABASE_NAME)
                            .addCallback(new RoomDatabase.Callback() {
                                @Override
                                public void onCreate(@NonNull SupportSQLiteDatabase db) {
                                    super.onCreate(db);
                                    Log.d(TAG, "Database initialized");
                                    String[] sqls = initDatabaseSQL();
                                    if (sqls != null) {
                                        for (String sql : sqls) {
                                            db.execSQL(sql);
                                            Log.d(TAG, "Executed SQL: " + sql);
                                        }
                                        Log.d(TAG, "Initialization complete");
                                    }
                                }
                            })
                            .allowMainThreadQueries()
                            .build();
                }
            }
        }
        return INSTANCE;
    }

    /**
     * Read the SQL statements from the raw resource file and split them into individual statements.
     *
     * @return An array of SQL statements.
     */
    public static String[] initDatabaseSQL() {
        Log.d(TAG, "Importing database");
        List<String> sqlList = new ArrayList<>();
        try (InputStream io = context.getResources().openRawResource(R.raw.init);
             BufferedReader br = new BufferedReader(new InputStreamReader(io))) {
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            String[] sqls = sb.toString().split(";");
            for (String sql : sqls) {
                if (!sql.trim().isEmpty()) {
                    sqlList.add(sql.trim());
                    Log.d(TAG, "Read SQL: " + sql.trim());
                }
            }
        } catch (IOException e) {
            Log.e(TAG, "Error reading SQL initialization file", e);
        }

        return sqlList.toArray(new String[0]);
    }
}